<?php
/**
 * Created by PhpStorm.
 * User: Qch
 * Date: 2017/2/28
 * Time: 20:15
 */

namespace Application\Common\Controller;


use J\Schema\IElement;
use J\Schema\ICollection;
use J\Schema\Syntax;
use J\Exception;
use J\Intent;
use J\MVC\Controller;
use J\Util\Tree;

class ElementUI
{
    /**
     * @var Controller
     */
    protected $controller;

    /**
     * @var ICollection
     */
    protected $model;

    /**
     * @var Intent
     */
    protected $intent;

    /**
     * Vue constructor
     *
     * @param  Controller $controller
     * @param Intent $intent
     * @param ICollection $model
     */
    public function __construct($controller, $intent, $model)
    {
        $this->controller = $controller;
        $this->model =  $model;
        $this->intent = $intent;
    }

    /**
     * @param  Controller $controller
     * @param Intent $intent
     * @param array $config
     * @param ICollection $model
     * @return array
     */
    static public function getView($controller, $intent, $config, $model)
    {
        $vue = new static($controller, $intent, $model);
        // 获取视图定义
        $schema = $vue->createView($config);
        // 扩展自定义项
        $schema = array_merge($schema, array_isset($config, 'mate', []));

        return [
            'view' => $schema,
            'title' => array_isset(
                $config,
                'title',
                $vue->model->getLabel() . '中心'
            )
        ];
    }

    /**
     * 视图名称
     * @param array $config
     * @return string
     */
    protected function getViewType($config)
    {
        return array_isset($config, Syntax::KEY_VIEW, Syntax::DISPLAY_TABLE);
    }

    /**
     * 创建单个视图
     *
     * @param array $config
     * @return array
     * @throws Exception
     */
    protected function createView($config)
    {
        $view = $this->getViewType($config);

        $schema = [];

        // 元数据
        switch ($view){
            // 表格
            case Syntax::DISPLAY_TABLE :
            // 树
            case Syntax::DISPLAY_TREE  :
                if ($this->model->isTree()) $view = Syntax::DISPLAY_TREE;
                $schema = $this->table($config);
                break;
            // 多选
            case Syntax::DISPLAY_SELECT:
                $schema = $this->selectView($config);
                break;
            // 表单
            case Syntax::DISPLAY_FORM:
                $schema = $this->form($config);
                $schema['style'] = array_isset($config, 'style', ['width' => '800px']);
                break;
            // 图表
            case Syntax::DISPLAY_CHART:
                $schema = $this->chart($config);
                break;
        }

        // 视图
        $schema['name'] = $view;

        return array_merge($config, $schema);
    }

    /**
     * 创建一个表格/树
     * @param array $config
     * @return array
     * @throws Exception
     */
    protected function table($config = [])
    {
        $mate = [];

        // 列信息
        $lines = [];
        $drawer = function (IElement $element) use (&$lines, $config) {
            $name = $element->getName();
            $lines[$name] = [
                'name' => $name,
                'index' => $name,
                'label' => $element->getLabel()
            ];
            if (isset($config['sort']) and in_array($name, $config['sort'])) {
                $lines[$name]['sortable'] = true;
            }
            $width = $element->getAttribute()->get('width', 0);
            if ($width > 0) $lines[$name]['width'] = $width;

            $span = $element->getAttribute()->get('span', 0);
            if ($span > 0) $lines[$name]['span'] = $span;
        };
        $this->model->elements()->iterateElements($drawer, get_elements($config));
        $mate['columns'] = array_values($lines);

        // 数据接口
        $mate['data'] = $this->createUrl('/data.php', 'data', $config);

        // 按钮
        $mate['btns'] = $this->toolbar($config, 'btns');

        // 行内操作
        $mate['actions'] = $this->toolbar($config, 'actions');

        // 查询表单
        if(isset($config['search'])){
            $mate['search']   = $this->inline($config['search']);
        }

        return $mate;
    }

    /**
     * 创建表单
     * @param array $config 表单配置
     * @param bool $inline 内部表单
     * @return array
     * @throws Exception
     */
    protected function form($config, $inline = false)
    {
        $mate = [];

        // 提交成功后跳转的地址
        if (!$inline) $mate['after'] = $this->createAfterUrl($config);
        // 标识为内置表单
        if ($inline) $mate['inline'] = $inline;
        // 创建表单域
        $columns = get_elements($config);
        // 表单数据
        $data = ($inline) ? [] : $this->row($config, $columns);

        $drawer = function (IElement $element) use (&$mate, $inline, $config, $data) {
            // 表单域类型
            $holder = $element->getFieldType();
            if ($holder === Syntax::FIELD_INNER) return;
            // 表单域名称
            $name = $element->getFieldName();
            // 数据
            $default = (!$inline and $element->hasDefault()) ? $element->getDefault() : null;
            $mate['values'][$name] = array_isset(
                $data,
                $element->getName(),
                $default
            );

            // 元素
            $field = [
                'holder' => $holder,
                'name' => $name,
                'placeholder' => $element->getAttribute()->get('placeholder', '请输入' . $element->getLabel())
            ];

            switch ($holder) {
                case 'radio':
                case 'checkbox':
                case 'select':
                    $options = array_values($element->getOptions(
                        $this->controller->getFilter()->getWhereOfFieldOption($element->getName())
                    ));
                    // 构造树状结构
                    $key = $this->model->getTreeKey();
                    if ($key) {
                        $field['holder'] = 'cascader';
                        $tree = new Tree();
                        $tree->fromList($options, $key);
                        $options = $tree->getRoot()->map(null, 'children');
                    }
                    $field['options'] = $options;
                    break;
                case 'switch':
                    $options = $element->getOptions();
                    $field['on'] = array_isset($options, 'on', '开启');
                    $field['off'] = array_isset($options, 'off', '关闭');
                    break;
            }

            if (!array_isset($config, 'inline', false)) {
                // label
                $field['label'] = $element->getLabel();
                // 验证
                $r = make_element_validator($element, $holder);
                if (!empty($r)) $mate['rules'][$name] = $r;
            }

            $mate['fields'][] = $field;
        };
        $this->model->elements()->iterateElements($drawer, $columns);

        // 组成表单提交地址
        $url = $this->createApiUrl($config);
        // 表单的ID参数
        if (array_isset($config, 'row', false) and isset($_REQUEST[Syntax::KEY_ID])) {
            $id = (int)$_REQUEST[Syntax::KEY_ID];
            $url .= '/' . Syntax::KEY_ID . '/' . $id;
        }

        $mate['url'] = $url;

        $mate['btns'] = $this->toolbar($config, 'btns');

        return $mate;
    }

    /**
     * 创建内联查询表单
     * @param $config
     * @return array
     * @throws Exception
     */
    protected function inline($config)
    {
        return $this->form($config, true);
    }

    /**
     * 图表
     * @param array $config
     * @return array
     */
    protected function chart($config)
    {
        // TODO 实现图表返回
        return $config;
    }

    /**
     * 创建一个按钮
     * @param string $action
     * @param array  $btn
     * @return array
     */
    protected function btn($action, $btn = [])
    {
        $r = $btn;
        $r['action'] = array_isset($btn, 'action', $action);

        switch ($action){
            case 'add' :
                $r['next'] = $this->createNextUrl($btn, 'Add');
                $r['label'] = array_isset($btn, 'label', '增加' . $this->model->getLabel());
                $r['type']     = "primary";
                break;
            case 'deleteSelected' :
                $r['url'] = $this->createApiUrl($btn, 'Delete');
                $r['label'] = array_isset($btn, 'url', '删除' . $this->model->getLabel());
                $r['type']  = 'danger';
                // $r['api']   = true;
                // $r['key']   = 'id';
                $r['multiSelect'] = true;
                $r['confirm'] = array_isset($btn, 'msg', "确定删除选中数据？");
                break;
            case 'delete' :
                $r['url'] = $this->createApiUrl($btn, 'Delete');
                // $r['path']  = $this->controller->getUrlString('Delete');
                $r['label'] = array_isset($btn,'url','删除');
                $r['type']     = 'danger';
                // $r['api']    = true;
                // $r['key']    = 'id';
                $r['confirm'] = array_isset($btn, 'msg', "确定删除本条数据？");
                break;
            case 'edit' :
                $r['next'] = $this->createNextUrl($btn, 'Edit');
                // $r['path']  = $this->controller->getUrlString('Edit');
                $r['label'] = array_isset($btn,'url','修改');
                $r['type']  = "success";
                // $r['key']   = 'id';
                break;
            case 'redirect' :
                $r['next'] =  $this->createNextUrl($btn);
                break;
            default:
                $action = array_isset($btn, 'action', null);

                if (!isset($btn['next'])) {
                    $r['url'] = $this->createApiUrl($btn, $action);
                    if (isset($btn['after']))
                        $r['after'] = $this->createAfterUrl($btn);
                } else {
                    $r['next'] = $this->createNextUrl($btn);
                }

                $r['label'] = array_isset($btn, 'label', $r['action']);
                $r['type'] = array_isset($btn, 'type', 'primary');

                break;
        }

        return $r;
    }

    /**
     * 创建按钮组
     *
     * @param array $config
     * @param string $key
     * @return array
     */
    protected function toolbar($config, $key)
    {
        $toolbar = [];

        if (isset($config[$key]['items'])) {
            foreach ($config[$key]['items'] as $action => $btn) {
                $toolbar['items'][] = $this->btn($action, $btn);
            }
        }

        return $toolbar;
    }

    /**
     * 组装视图Url
     *
     * /file.php/path/key/value
     *
     * @param array $config 配置
     * @param string $action mvc设置
     *
     * @return string
     */
    protected function createNextUrl($config, $action = null)
    {
        return $this->createUrl('/element.php', 'next', $config, $action);
    }

    /**
     * 组装ApiUrl
     *
     * @see $this->createElementUrl
     *
     * @param array $config 配置
     * @param string $action mvc设置
     *
     * @return string
     */
    protected function createApiUrl($config, $action = null)
    {
        return $this->createUrl('/api.php', 'api', $config, $action);
    }

    /**
     * 组装跳转接口
     *
     * @param array $config 配置
     * @param string $action mvc设置
     *
     * @return string
     */
    protected function createAfterUrl($config, $action = 'Index')
    {
        return $this->createUrl('/element.php', 'after', $config, $action,false);
    }

    /**
     * 组装Url
     *
     * /file.php/path/key/value
     *
     * @param string $file 脚本名
     * @param string $key 键名
     * @param array $config 配置
     * @param string $action mvc设置
     * @param bool $param 是否处理参数
     *
     * @return string
     */
    protected function createUrl($file, $key, $config, $action = null, $param = true)
    {
        // 组装路径
        $url = array_isset($config, $key, null);
        if (is_array($url)) {
            $url = $this->intent->makeUrl($url, $file);
        } elseif (!$url) {
            $url  = $this->intent->makeUrl($action, $file);
        }

        // return $url;

        if (!$param) return $url;

        // 组装参数
        $param = self::param(array_isset($config, Syntax::KEY_QUERY, false));

        return $url . $param;
    }

    /**
     * 获取指定条目数据，用于修改类表单
     *
     * @param array $config
     * @param string $columns
     * @throws Exception
     *
     * @return array
     */
    protected function row($config, $columns = Syntax::KEY_ALL)
    {
        $values = [];
        if (array_isset($config, 'row', false) and isset($_REQUEST[Syntax::KEY_ID])) {
            $id = (int)$_REQUEST[Syntax::KEY_ID];
            $values = $this->model->getCurd()->setIds($id)->setElements($columns)->setRaw(true)->find();
            // $data = $values[$id];
        }
        return $values;
    }

    /**
     * 创建URL查询子句
     * @param $param
     * @return string
     */
    protected static function param($param)
    {
        if (empty($param)) return '';

        if ($param === true) $param = Syntax::KEY_ID;

        if(is_string($param)) $param = [$param];

        $str = '';

        foreach ($param as $key => $p){
            if(is_string($key)){
                $str .= "/$key/".urlencode($_REQUEST[$p]);
            }else{
                $str .= "/$p/".urlencode($_REQUEST[Syntax::KEY_ID]);
            }
        }

        return $str;
    }

    /**
     * 选择视图
     * @param array $config
     * @return array
     */
    protected function selectView($config)
    {
        return [
            'url' => $this->createApiUrl($config)
        ];
    }
}